import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 一个有趣的粒子系统模拟程序。
 * 展示了面向对象编程、GUI、动画和简单的物理效果。
 */
public class ParticleSystem extends JFrame {

    // 粒子系统的主面板
    private ParticlePanel particlePanel;

    public ParticleSystem() {
        super("粒子系统模拟");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 创建并添加粒子面板
        particlePanel = new ParticlePanel();
        add(particlePanel);

        // 设置窗口大小并使其可见
        setSize(800, 600);
        setLocationRelativeTo(null); // 居中显示
        setVisible(true);

        // 启动动画循环
        particlePanel.startAnimation();
    }

    /**
     * 程序的入口点
     */
    public static void main(String[] args) {
        // 在事件调度线程（EDT）中创建GUI，确保线程安全
        SwingUtilities.invokeLater(() -> new ParticleSystem());
    }

    /**
     * 粒子面板，负责渲染和更新所有粒子
     */
    private class ParticlePanel extends JPanel {

        private List<Particle> particles;
        private final int INITIAL_PARTICLES = 50; // 初始粒子数量
        private final double GRAVITY = 0.2;      // 重力加速度
        private boolean isRunning;
        private boolean isInitialized = false; // 新增一个标志位，确保初始化只进行一次

        public ParticlePanel() {
            setBackground(Color.BLACK);
            particles = new ArrayList<>();
            isRunning = true;

            // 【修正点】添加组件监听器，以正确处理初始化
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    // 当组件大小确定后，如果还未初始化，则进行初始化
                    if (!isInitialized) {
                        initializeParticles();
                        isInitialized = true; // 设置标志位，防止重复初始化
                    }
                    repaint(); // 尺寸变化后重绘
                }
            });

            // 添加鼠标监听器，点击时创建新粒子
            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    // 在鼠标点击位置创建10个新粒子
                    for (int i = 0; i < 10; i++) {
                        particles.add(new Particle(e.getX(), e.getY()));
                    }
                }
            });
        }

        /**
         * 【新增方法】初始化粒子
         * 这个方法会在组件尺寸确定后被调用
         */
        private void initializeParticles() {
            Random random = new Random();
            // 现在可以安全地获取面板的宽度和高度了
            int panelWidth = getWidth();
            int panelHeight = getHeight();

            for (int i = 0; i < INITIAL_PARTICLES; i++) {
                int x = random.nextInt(panelWidth);
                int y = random.nextInt(panelHeight / 2); // 只在上半部分生成
                particles.add(new Particle(x, y));
            }
        }

        /**
         * 启动动画循环
         */
        public void startAnimation() {
            Thread animationThread = new Thread(() -> {
                while (isRunning) {
                    // 【修正点】只有在初始化完成后才更新粒子，避免空指针或无效状态
                    if (isInitialized) {
                        updateParticles();
                    }
                    repaint(); // 触发 paintComponent 方法进行重绘
                    try {
                        Thread.sleep(16); // 大约每秒60帧 (1000/60 ≈ 16)
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            animationThread.start();
        }

        /**
         * 更新所有粒子的位置和状态
         */
        private void updateParticles() {
            for (Particle p : particles) {
                // 应用重力，增加y方向的速度
                p.velocityY += GRAVITY;

                // 更新位置
                p.x += p.velocityX;
                p.y += p.velocityY;

                // 边界碰撞检测和处理 (左右墙壁)
                if (p.x <= p.radius || p.x >= getWidth() - p.radius) {
                    p.velocityX *= -0.9; // 反弹并损失一些能量
                }

                // 边界碰撞检测和处理 (上下墙壁)
                if (p.y <= p.radius || p.y >= getHeight() - p.radius) {
                    p.velocityY *= -0.9; // 反弹并损失一些能量
                    // 防止粒子卡在边界
                    if (p.y >= getHeight() - p.radius) {
                        p.y = getHeight() - p.radius;
                    }
                    if (p.y <= p.radius) {
                        p.y = p.radius;
                    }
                }
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            // 开启抗锯齿，让粒子边缘更平滑
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            // 【修正点】只有在初始化完成后才绘制粒子
            if (isInitialized) {
                for (Particle p : particles) {
                    g2d.setColor(p.color);
                    g2d.fillOval((int) (p.x - p.radius), (int) (p.y - p.radius),
                            (int) (2 * p.radius), (int) (2 * p.radius));
                }

                // 绘制 instructions
                g2d.setColor(Color.WHITE);
                g2d.drawString("点击鼠标创建新粒子 | 粒子数量: " + particles.size(), 10, 20);
            }
        }

        @Override
        public void setSize(int width, int height) {
            super.setSize(width, height);
            // 确保粒子面板也调整大小
            if (particlePanel != null) {
                particlePanel.setPreferredSize(new Dimension(width, height));
            }
        }
    }

    /**
     * 粒子类，封装了单个粒子的属性和行为
     */
    private static class Particle {
        double x, y; // 位置
        double velocityX, velocityY; // 速度
        double radius; // 半径
        Color color; // 颜色
        private static final Random random = new Random();

        public Particle(double x, double y) {
            this.x = x;
            this.y = y;
            this.radius = 5 + random.nextDouble() * 15; // 随机半径 5-20
            // 随机初始速度
            this.velocityX = (random.nextDouble() - 0.5) * 5;
            this.velocityY = (random.nextDouble() - 0.5) * 5;
            // 随机颜色
            this.color = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256), 200);
        }
    }
}